import json
import time
from datetime import datetime
from django.utils import timezone
from django.shortcuts import render

# Create your views here.
from django.views import View
from utils.views import LoginRequiredJSONMixin
from apps.users.models import Address
from apps.goods.models import SKU
from apps.orders.models import OrderInfo, OrderGoods
from django_redis import get_redis_connection
from django.http import JsonResponse
from decimal import Decimal
# 事务有三个点(开始点，回滚点，提交点)
from django.db import transaction

class OrderDetailView(LoginRequiredJSONMixin, View):
    def get(self, request):
        # 1. 获取用户信息
        user = request.user
        # 2. 获取地址信息
        address_data = list()
        addresses = Address.objects.filter(user=user, is_deleted=False)

        for address in addresses:
            address_data.append({
                "id": address.id,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                'receiver': address.receiver,
                "mobile": address.mobile,
            })

        # 3. 获取选中商品信息
        redis_cli = get_redis_connection('carts')
        pipeline = redis_cli.pipeline()
        # warning: 不要在这里接收 redis的响应
        pipeline.hgetall(f'carts_{user.id}')
        pipeline.smembers(f'selected_{user.id}')
        # 我们接收 管道统一执行后的返回结果
        result = pipeline.execute()
        sku_id_counts = result[0]
        selected_ids = result[1]
        selected_detail = dict()

        for sku_id, sku_count in sku_id_counts.items():
            if sku_id in selected_ids:
                selected_detail[int(sku_id)] = {'count': int(sku_count), 'selected': True}

        sku_data = list()
        for sku_id, count_selected_dict in selected_detail.items():
            sku = SKU.objects.get(pk=sku_id)
            sku_data.append({
                'id': sku.id,
                'name': sku.name,
                'count': count_selected_dict['count'],
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })

        context = {
            'skus': sku_data,
            'addresses': address_data,
            # decimal: 货币类型，保存字符串类型数据，精度不会缺失
            'freight': Decimal('10.00')  # 运费
        }
        return JsonResponse({'code': '0', 'errmsg': 'ok', 'context': context})


class OrderCommitView(LoginRequiredJSONMixin, View):
    def post(self, request):
        parameters = json.loads(request.body.decode())
        address_id = parameters.get('address_id')
        pay_method = parameters.get('pay_method')

        if not all([address_id, pay_method]):
            return JsonResponse({'code': 400, 'errmsg': '缺少必填参数'})

        if pay_method not in [OrderInfo.PAY_METHODS_ENUM['CASH'], OrderInfo.PAY_METHODS_ENUM['ALIPAY']]:
            return JsonResponse({'code': 0, 'errmsg': '参数pay_method错误'})

        try:
            address = Address.objects.get(pk=address_id)
        except Address.DoesNotExist:
            return JsonResponse({'code': 0, 'errmsg': '该地址不存在'})

        user = request.user

        # order_id 主键(自己生成)    年月日时分秒  +  用户id(9位数字)
        # 注意：%的格式化形式可以生成指定位数的编号，f  {}  的格式化形式无法生成指定位数的编号
        # %f: 微妙
        order_id = timezone.localtime().strftime('%Y%m%d%H%M%S%f') + '%09d' % user.id

        with transaction.atomic():
            # 事务开始点
            point = transaction.savepoint()

            order = OrderInfo.objects.create(
                order_id=order_id,
                user=user,
                address=address,
                total_count=0,
                total_amount=Decimal('0.0'),
                freight=Decimal('10.0'),
                pay_method=pay_method,
                status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else \
                    OrderInfo.ORDER_STATUS_ENUM['UNSEND']
            )

            total_count = 0
            total_amount = 0
            redis_cli = get_redis_connection('carts')
            pipeline = redis_cli.pipeline()
            pipeline.hgetall(f'carts_{user.id}')
            pipeline.smembers(f'selected_{user.id}')
            result = pipeline.execute()

            sku_id_counts = result[0]
            selected_ids = result[1]

            selected_skus = dict()
            for sku_id in selected_ids:
                selected_skus[int(sku_id)] = int(sku_id_counts[sku_id])

            for sku_id, count in selected_skus.items():
                for _ in range(10):
                    sku = SKU.objects.get(pk=sku_id)

                    if sku.stock < count:
                        # 事务回滚点
                        transaction.savepoint_rollback(point)
                        return JsonResponse({'code': 400, 'errmsg': '销量不足'})

                    # sku.stock -= count
                    # sku.sales += count
                    # 查询当前的库存
                    old_stock = sku.stock
                    new_stock = sku.stock - count
                    new_sale = sku.sales + count
                    result = SKU.objects.filter(pk=sku_id, stock=old_stock).update(stock=new_stock, sales=new_sale)
                    # result = 1  表示有一条记录修改成功
                    # result = 0 表示没有更新
                    if result == 0:
                        time.sleep(0.000005)
                        continue
                        # transaction.savepoint_rollback(point)
                        # return JsonResponse({'code': 400, 'errmsg': '下单失败'})

                    OrderGoods.objects.create(
                        order=order,
                        sku=sku,
                        count=count,
                        price=sku.price
                    )
                    break

                total_count += count
                total_amount += count * sku.price

            order.total_count = total_count
            order.total_amount = Decimal(total_amount)

            order.save()
            # 事务提交点
            transaction.savepoint_commit(point)

        # 清除购物车已结算的商品
        pipeline.hdel(f'carts_{user.id}', *selected_ids)
        pipeline.srem(f'selected_{user.id}', *selected_ids)
        pipeline.execute()
        return JsonResponse({'code': 0, 'errmsg': 'ok', 'order_id': order_id})


"""
解决超卖问题

① 队列
② 锁
    悲观锁：  当查询某条记录时，即让数据库为该记录加锁，锁住记录后别人无法操作
             悲观锁类似于我们在多线程资源竞争时添加的互斥锁，容易出现死锁现象
        
    乐观锁：  乐观锁并不是真实存在的锁。
             在更新的时候判断此时的库存是否是之前查询出的库存，
             如果相同，表示没人修改，可以更新库存，否则表示别人抢过资源，不再执行库存更新。

MySQL数据库事务隔离级别主要有四种：
    Serializable：串行化，一个事务一个事务的执行。
    Repeatable read：可重复读，无论其他事务是否修改并提交了数据，在这个事务中看到的数据值始终不受其他事务影响。
    Read committed：读取已提交，其他事务提交了对数据的修改后，本事务就能读取到修改后的数据值。
    Read uncommitted：读取未提交，其他事务只要修改了数据，即使未提交，本事务也能看到修改后的数据值。
MySQL数据库默认使用可重复读（ Repeatable read）。
"""
